package xyz.lwm.lazycat.utility.metaobject;

import org.jetbrains.annotations.NotNull;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.convert.Convert;
import xyz.lwm.lazycat.utility.convert.Types;
import xyz.lwm.lazycat.utility.exception.UtilException;
import xyz.lwm.lazycat.utility.reflect.ReflectUtil;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

/**
 * @author lwm
 */
class BeanObject implements MetaObject {

    private final Object bean;

    private final Map<String, Method> getters;

    private final Map<String, Method> setters;

    private final List<String> propertyNames;

    private final MetaConfig config;

    public BeanObject(Object bean, MetaConfig config) {
        Class<?> clz = bean.getClass();
        this.bean = bean;
        getters = ReflectUtil.findGetters(clz);
        setters = ReflectUtil.findSetters(clz);
        LinkedHashSet<String> names = new LinkedHashSet<>();
        names.addAll(getters.keySet());
        names.addAll(setters.keySet());
        propertyNames = new ArrayList<>(names);
        this.config = config;
    }

    @NotNull
    @Override
    public List<String> findPropertyNames() {
        return propertyNames;
    }

    @Override
    public Object getValue(@NotNull String propertyName) {
        return It.of(getters.get(propertyName))
                .let(m -> ReflectUtil.invoke(bean, m))
                .getOr(null);
    }

    @Override
    public void setValue(@NotNull String propertyName, Object value) {
        It.of(setters.get(propertyName)).also(m -> {
            Object finalValue = value;
            // 非空值且允许转换类型时, 尝试转换
            if (value != null && config.isAutoConvert()) {
                Class<?> setterType = m.getParameterTypes()[0];
                Class<?> valType = value.getClass();
                // 基本类型且类型不一致时, 尝试转换
                if (setterType != valType && Types.isBaseType(setterType) && Types.isBaseType(valType)) {
                    finalValue = Convert.fromString(Convert.toString(value), setterType);
                }
            }
            try {
                ReflectUtil.setValue(bean, m, finalValue);
            } catch (Exception e) {
                if (!config.isContinueOnerror()) {
                    throw new UtilException(e);
                }
            }
        });
    }

}
